package com.example.lte.service;

import com.example.lte.entity.UserEntity;
import com.example.lte.repo.UserRepo;
import org.springframework.retry.RetryException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.time.LocalDateTime;

@Service
public class TestRetryService {


    /**
     *
     *
     * 请求http://localhost:8081/lte/testRetry?account=123
     *  @Retryable 标注的方法 会执行3次重试  ，
     *  第3次失败后，使用@Recover标注的方法兜底，即返回 预制的假数据/错误数据
     * {
     *   "code": 0,
     *   "msg": "操作成功",
     *   "data": {
     *     "id": 1607182481902,
     *     "sex": 0,
     *     "name": "retry",
     *     "account": "123",
     *     "password": "",
     *     "ddLoginCode": "",
     *     "addTime": "2020-12-05 23:34:41",
     *     "updateTime": "2020-12-05 23:34:41",
     *     "version": 0
     *   },
     *   "timestamp": 1607182481907
     * }
     *
     */

    public   int count = 0;

    public long t = 0;

    @Resource
    private UserRepo userRepo;

    //recover：指定兜底/补偿的方法名。
    // 如果不指定，默认对照 @Recover 标识的，第一入参为重试异常，其余入参和出参一致的方法；
    //interceptor：指定方法切面 bean，org.aopalliance.intercept.MethodInterceptor 实现类
    //value / include：两者用途一致，指出哪些类型需要重试；
    //exclude：和 include 相反，指出哪些异常不需要重试；
    //label：可以指定唯一标签，用于统计；
    //stateful：默认false。重试是否是有状态的；
    //maxAttempts：最大的重试次数；
    //backoff：指定 @Backoff，回退策略；
    //listeners：指定 org.springframework.retry.RetryListener 实现 bean。
    //
    //@Backoff 的属性如下，基本都对应上面编程式的几种策
    //value / delay：两者都标识延迟时间，为 0则对应 NoBackOffPolicy 策略。
    //maxDelay：最大延迟时间
    //multiplier：递增乘数
    //random：递增乘数是否随机
//    @Retryable(include = RetryException.class,
//            exclude = IllegalArgumentException.class,
//            listeners = {"defaultRetryListener"},
//            backoff = @Backoff(delay = 1000, maxDelay = 2000))
    @Retryable(value = RetryException.class,
            maxAttempts = 3,  //这里指定了第3次失败后，使用@Recover标注的方法兜底
            backoff = @Backoff(delay = 1000 * 2, multiplier = 1.5))
    public UserEntity  queryByAccount(String account){
        count++;
        long s  = System.currentTimeMillis();
        t = s;

        if (account.startsWith("123")){
            System.err.println("第"+count+"次尝试"+System.currentTimeMillis());
            throw new RetryException("retry3次重试失败");
        } else {
           return userRepo.findByAccount(account);
        }
    }

    /**
     * 兜底，多次重试失败后，返回预制的假数据/错误数据
     * @param e RetryException
     * @param account String
     * @return UserEntity
     */
    @Recover
    public UserEntity  recover(RetryException e, String account){
        return UserEntity.builder()
                .account(account)
                .name(e.getMessage())
                .id(System.currentTimeMillis())
                .addTime(Timestamp.valueOf(LocalDateTime.now()))
                .updateTime(Timestamp.valueOf(LocalDateTime.now()))
                .ddLoginCode("")
                .password("")
                .sex(0)
                .version(0L)
                .build();
    }
}
